/*
 * Copyright (c) 2008-2012
 * LANIT
 * All rights reserved.
 *
 * This product and related documentation are protected by copyright and
 * distributed under licenses restricting its use, copying, distribution, and
 * decompilation. No part of this product or related documentation may be
 * reproduced in any form by any means without prior written authorization of
 * LANIT and its licensors, if any.
 *
 * $Id$
 */
package ru.lanit.bpm.ipa.timesheet;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import ru.lanit.bpm.ipa.timesheet.dao.EmployeeDao;
import ru.lanit.bpm.ipa.timesheet.model.Employee;

import java.io.IOException;
import java.io.Reader;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;

public class DictionaryUpdater {
    private static final Logger logger = LoggerFactory.getLogger(DictionaryUpdater.class);
    private static final String MAIL_KEY = "Mail";
    private static final String PYUSERIDENTIFIER_KEY = "pyUserIdentifier";

    private EmployeeDao employeeDao;
    private Settings settings;
    private Reader readerMrpPath;
    private Map<String, Collection<String>> requisities;

    private String currentEmployee;
    private String currentSpec;
    private String currentProject;
    private int lineNumber;

    public DictionaryUpdater(EmployeeDao employeeDao, Settings settings, Reader readerMrpPath, Map<String, Collection<String>> requisities) {
        this.employeeDao = employeeDao;
        this.settings = settings;
        this.readerMrpPath = readerMrpPath;
        this.requisities = requisities;
    }

    public void updateDictionaries() throws IOException {
        Iterator<String> iterator;
        iterator = IOUtils.lineIterator(readerMrpPath);
        lineNumber = 0;
        while (iterator.hasNext()) {
            lineNumber++;

            Statement statement = processStatement(iterator.next());
            logger.info("Parsed {}", statement);
            statement.process();
        }
    }

    // ===================================================================================================================
    // = Implementation
    // ===================================================================================================================
    private Statement processStatement(String line) {
        String[] values = StringUtils.delimitedListToStringArray(line, ";");
        String phrase = values[0];
        if (lineNumber < 3 || settings.getTrashes().contains(phrase)) {
            return new TrashStatement(phrase);
        } else if (settings.getSpecs().contains(phrase)) {
            return new SpecStatement(phrase);
        } else if (settings.getProjects().contains(phrase)) {
            return new ProjectStatement(phrase);
        } else {
            return new EmployeeStatement(phrase);
        }
    }

    private void addEmployee() {
        //        String division = settings.getSpec2division().get(currentSpec);
        String[] names = StringUtils.delimitedListToStringArray(currentEmployee, " ");

        String key = getMapKey(currentEmployee);
        Collection<String> details = requisities.get(key);
        String[] detail = new String[2];
        details.toArray(detail);
        Employee employee = new Employee(null, currentEmployee, detail[0],
                detail[1], safeArrayValue(names, 0), safeArrayValue(names, 1), safeArrayValue(names, 2));
       /* System.out.println("currentEmployee = " + currentEmployee);
        System.out.println("detail[1] = " + detail[1]);
        System.out.println("detail[0] = " + detail[0]);*/

        employeeDao.add(employee);
    }

    private void addEmployeeToDivisionOD() {
        String division = settings.getSpec2division().get(currentSpec);
        employeeDao.addEmployeeToOD(currentEmployee, division, settings.getDivisionRole());
    }

    private void addEmployeeToNonWorkingTimeOD() {
        employeeDao.addEmployeeToOD(currentEmployee, settings.getNonWorkTimeOD(), settings.getNonWorkTimeRole());
    }

    private void addEmployeeToProjectODs() {
        Collection<String> ods = settings.getProject2ODs().get(currentProject);
        if (currentSpec.equals("Руководитель проектов")) {
            logger.info("PM skip");
            return;
        }
        for (String od : ods) {
            if (od.equals(settings.getSpec2division().get(currentSpec))) {
                logger.info("Line {}. Skipping project {} as same as division for {}", new Object[]{lineNumber, od, currentEmployee});
                continue;
            }
            employeeDao.addEmployeeToOD(currentEmployee, od, settings.getProjectRole());
        }
    }

    private String safeArrayValue(String[] array, int index) {
        return array.length > index ? array[index] : null;
    }

    private String getMapKey(String name) {
        for (String key : requisities.keySet()) {
            if (name.equals(key) || name.contains(key) || key.contains(name)) {
                return key;
            }
        }
        return null;
    }

    private abstract class Statement {
        protected final String phrase;

        public Statement(String phrase) {
            this.phrase = phrase;
        }

        public abstract void process();

        @Override
        public String toString() {
            return ReflectionToStringBuilder.toString(this);
        }
    }

    private class EmployeeStatement extends Statement {
        public EmployeeStatement(String phrase) {
            super(phrase);
        }

        @Override
        public void process() {
            currentEmployee = phrase;
            if (employeeDao.findByName(currentEmployee) == null) {
                addEmployee();
                addEmployeeToDivisionOD();
                addEmployeeToNonWorkingTimeOD();
            } else {
                logger.info("Line {}. Skipping employee creation for duplicate {}", lineNumber, currentEmployee);
            }
        }
    }

    private class ProjectStatement extends Statement {
        public ProjectStatement(String phrase) {
            super(phrase);
        }

        @Override
        public void process() {
            currentProject = phrase;
            addEmployeeToProjectODs();
        }
    }

    private class SpecStatement extends Statement {
        public SpecStatement(String phrase) {
            super(phrase);
        }

        @Override
        public void process() {
            currentSpec = phrase;
        }
    }

    private class TrashStatement extends Statement {
        public TrashStatement(String phrase) {
            super(phrase);
        }

        @Override
        public void process() {
            // do nothing
        }
    }
}
